﻿using System;

namespace CamlQueryCreator.Common
{
    /// <summary>
    /// Соединяющий элемент ("И", "ИЛИ", "Where") CAML-запроса.
    /// </summary>
    public abstract class CamlConnector : CamlElement, ICamlConnector
    {
        /// <summary>
        /// Название поля "Идентификатор типа контента".
        /// </summary>
        private const string CONTENT_TYPE_ID = "ContentTypeId";

        /// <summary>
        /// Название поля "Тип контента".
        /// </summary>
        private const string CONTENT_TYPE = "ContentType";

        private const string FIELD_TYPE_NOT_LOOKUP_AND_NOT_USER_ERROR_MESSAGE = "\"fieldInfo.FieldType\" must by \"FieldTypeKind.Lookup\" or \"FieldTypeKind.User\"";
        private const string FIELD_TYPE_NOT_TEXT_AND_NOT_NOTE_ERROR_MESSAGE = "\"fieldInfo.FieldType\" must by \"FieldTypeKind.Text\" or \"FieldTypeKind.Note\"";
        private const string FIELD_TYPE_ERROR_MESSAGE_FORMAT = "\"fieldInfo.FieldType\" must by \"{0}\"";

        /// <summary>
        /// Соединяющий элемент ("И", "ИЛИ", "Where") CAML-запроса.
        /// <para>Конструктор.</para>
        /// </summary>
        protected CamlConnector(CamlBase parentBuilder): base(parentBuilder)
        {
        }

        #region Implementation of ICamlConnector

        #region Operators

        CamlElement ICamlConnector.ContentTypeIdEq(string contentTypeID)
        {
            return new CamlOperatorCommon(this, CONTENT_TYPE_ID, FieldTypeKind.ContentTypeId, contentTypeID, OperationKind.Eq);
        }

        CamlElement ICamlConnector.ContentTypeIdNeq(string contentTypeID)
        {
            return new CamlOperatorCommon(this, CONTENT_TYPE_ID, FieldTypeKind.ContentTypeId, contentTypeID, OperationKind.Neq);
        }

        public CamlElement ContentTypeEq(string contentTypeName)
        {
            return new CamlOperatorCommon(this, CONTENT_TYPE, FieldTypeKind.Computed, contentTypeName, OperationKind.Eq);
        }

        public CamlElement ContentTypeNeq(string contentTypeName)
        {
            return new CamlOperatorCommon(this, CONTENT_TYPE, FieldTypeKind.Computed, contentTypeName, OperationKind.Neq);
        }

        CamlElement ICamlConnector.CurrentUserEq(IListFieldInfoBase fieldInfo)
        {
            return new CamlOperatorCurrentUser(this, fieldInfo, OperationKind.Eq);
        }

        CamlElement ICamlConnector.CurrentUserEq(string fieldName)
        {
            return new CamlOperatorCurrentUser(this, fieldName, OperationKind.Eq);
        }

        CamlElement ICamlConnector.CurrentUserNeq(IListFieldInfoBase fieldInfo)
        {
            return new CamlOperatorCurrentUser(this, fieldInfo, OperationKind.Neq);
        }

        CamlElement ICamlConnector.CurrentUserNeq(string fieldName)
        {
            return new CamlOperatorCurrentUser(this, fieldName, OperationKind.Neq);
        }

        public CamlElement CurrentUserIncludes(IListFieldInfoBase fieldInfo)
        {
            return new CamlOperatorCurrentUser(this, fieldInfo, OperationKind.Includes);
        }

        public CamlElement CurrentUserIncludes(string fieldName)
        {
            return new CamlOperatorCurrentUser(this, fieldName, OperationKind.Includes);
        }

        public CamlElement CurrentUserNotIncludes(IListFieldInfoBase fieldInfo)
        {
            return new CamlOperatorCurrentUser(this, fieldInfo, OperationKind.NotIncludes);
        }

        public CamlElement CurrentUserNotIncludes(string fieldName)
        {
            return new CamlOperatorCurrentUser(this, fieldName, OperationKind.NotIncludes);
        }

        CamlElement ICamlConnector.Eq(IListFieldInfoBase fieldInfo, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Eq);
        }

        CamlElement ICamlConnector.Eq(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            var fieldValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Eq);
        }

        CamlElement ICamlConnector.Eq(string fieldName, FieldTypeKind fieldType, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldName, fieldType, fieldValue, OperationKind.Eq);
        }

        CamlElement ICamlConnector.Neq(IListFieldInfoBase fieldInfo, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Neq);
        }

        CamlElement ICamlConnector.Neq(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            var fieldValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Neq);
        }

        CamlElement ICamlConnector.Neq(string fieldName, FieldTypeKind fieldType, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldName, fieldType, fieldValue, OperationKind.Neq);
        }

        CamlElement ICamlConnector.Lt(IListFieldInfoBase fieldInfo, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Lt);
        }

        CamlElement ICamlConnector.Lt(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            var fieldValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Lt);
        }

        CamlElement ICamlConnector.Lt(string fieldName, FieldTypeKind fieldType, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldName, fieldType, fieldValue, OperationKind.Lt);
        }

        CamlElement ICamlConnector.Leq(IListFieldInfoBase fieldInfo, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Leq);
        }

        CamlElement ICamlConnector.Leq(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            var fieldValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Leq);
        }

        CamlElement ICamlConnector.Leq(string fieldName, FieldTypeKind fieldType, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldName, fieldType, fieldValue, OperationKind.Leq);
        }

        CamlElement ICamlConnector.Gt(IListFieldInfoBase fieldInfo, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Gt);
        }

        CamlElement ICamlConnector.Gt(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            var fieldValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Gt);
        }

        CamlElement ICamlConnector.Gt(string fieldName, FieldTypeKind fieldType, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldName, fieldType, fieldValue, OperationKind.Gt);
        }

        CamlElement ICamlConnector.Geq(IListFieldInfoBase fieldInfo, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Geq);
        }

        CamlElement ICamlConnector.Geq(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            var fieldValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, fieldValue, OperationKind.Geq);
        }

        CamlElement ICamlConnector.Geq(string fieldName, FieldTypeKind fieldType, object fieldValue)
        {
            return new CamlOperatorCommon(this, fieldName, fieldType, fieldValue, OperationKind.Geq);
        }

        CamlElement ICamlConnector.IsNull(IListFieldInfoBase fieldInfo)
        {
            return new CamlOperatorIsNull(this, fieldInfo, true);
        }

        CamlElement ICamlConnector.IsNull(string fieldName)
        {
            return new CamlOperatorIsNull(this, fieldName, true);
        }

        CamlElement ICamlConnector.IsNotNull(IListFieldInfoBase fieldInfo)
        {
            return new CamlOperatorIsNull(this, fieldInfo, false);
        }

        CamlElement ICamlConnector.IsNotNull(string fieldName)
        {
            return new CamlOperatorIsNull(this, fieldName, false);
        }

        CamlElement ICamlConnector.BeginsWith(IListFieldInfoBase fieldInfo, object selectedValue)
        {
            return new CamlOperatorCommon(this, fieldInfo, selectedValue, OperationKind.BeginsWith);
        }

        public CamlElement BeginsWith(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            var selectedValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, selectedValue, OperationKind.BeginsWith);
        }

        CamlElement ICamlConnector.BeginsWith(string fieldName, FieldTypeKind fieldType, object selectedValue)
        {
            return new CamlOperatorCommon(this, fieldName, fieldType, selectedValue, OperationKind.BeginsWith);
        }

        CamlElement ICamlConnector.Contains(IListFieldInfoBase fieldInfo, string selectedValue)
        {
            if (fieldInfo.CamlQueryFieldType != FieldTypeKind.Text &
                fieldInfo.CamlQueryFieldType != FieldTypeKind.Note)
                throw new ArgumentException(FIELD_TYPE_NOT_TEXT_AND_NOT_NOTE_ERROR_MESSAGE);
            return new CamlOperatorCommon(this, fieldInfo, selectedValue, OperationKind.Contains);
        }

        public CamlElement Contains(IListItemModel listItem, IListFieldInfoBase fieldInfo)
        {
            if (fieldInfo.CamlQueryFieldType != FieldTypeKind.Text &
               fieldInfo.CamlQueryFieldType != FieldTypeKind.Note)
                throw new ArgumentException(FIELD_TYPE_NOT_TEXT_AND_NOT_NOTE_ERROR_MESSAGE);

            var selectedValue = listItem.GetValue(fieldInfo);
            return new CamlOperatorCommon(this, fieldInfo, selectedValue, OperationKind.Contains);
        }

        CamlElement ICamlConnector.Contains(string fieldName, FieldTypeKind fieldType, string selectedValue)
        {
            if (fieldType != FieldTypeKind.Text &
                fieldType != FieldTypeKind.Note)
                throw new ArgumentException(FIELD_TYPE_NOT_TEXT_AND_NOT_NOTE_ERROR_MESSAGE);
            return new CamlOperatorCommon(this, fieldName, fieldType, selectedValue, OperationKind.Contains);
        }

        CamlElement ICamlConnector.Includes(IListFieldInfoBase fieldInfo, int listItemID)
        {
            if (fieldInfo.CamlQueryFieldType != FieldTypeKind.Lookup &&
                fieldInfo.CamlQueryFieldType != FieldTypeKind.User)
                throw new ArgumentException(FIELD_TYPE_NOT_LOOKUP_AND_NOT_USER_ERROR_MESSAGE);
            return new CamlOperatorCommon(this, fieldInfo.Name, listItemID, OperationKind.Includes, true);
        }

        CamlElement ICamlConnector.Includes(IListFieldInfoBase fieldInfo, string listItemName)
        {
            if (fieldInfo.CamlQueryFieldType != FieldTypeKind.Lookup &&
                fieldInfo.CamlQueryFieldType != FieldTypeKind.User)
                throw new ArgumentException(FIELD_TYPE_NOT_LOOKUP_AND_NOT_USER_ERROR_MESSAGE);
            return new CamlOperatorCommon(this, fieldInfo.Name, listItemName, OperationKind.Includes, false);
        }

        CamlElement ICamlConnector.Includes(string fieldName, int listItemID)
        {
            return new CamlOperatorCommon(this, fieldName, listItemID, OperationKind.Includes, true);
        }

        CamlElement ICamlConnector.Includes(string fieldName, string listItemName)
        {
            return new CamlOperatorCommon(this, fieldName, listItemName, OperationKind.Includes, false);
        }

        CamlElement ICamlConnector.NotIncludes(IListFieldInfoBase fieldInfo, int listItemID)
        {
            if (fieldInfo.CamlQueryFieldType != FieldTypeKind.Lookup &&
                fieldInfo.CamlQueryFieldType != FieldTypeKind.User)
                throw new ArgumentException(FIELD_TYPE_NOT_LOOKUP_AND_NOT_USER_ERROR_MESSAGE);
            return new CamlOperatorCommon(this, fieldInfo.Name, listItemID, OperationKind.NotIncludes, true);
        }

        CamlElement ICamlConnector.NotIncludes(IListFieldInfoBase fieldInfo, string listItemName)
        {
            if (fieldInfo.CamlQueryFieldType != FieldTypeKind.Lookup &&
                fieldInfo.CamlQueryFieldType != FieldTypeKind.User)
                throw new ArgumentException(FIELD_TYPE_NOT_LOOKUP_AND_NOT_USER_ERROR_MESSAGE);
            return new CamlOperatorCommon(this, fieldInfo.Name, listItemName, OperationKind.NotIncludes, false);
        }

        CamlElement ICamlConnector.NotIncludes(string fieldName, int listItemID)
        {
            return new CamlOperatorCommon(this, fieldName, listItemID, OperationKind.NotIncludes, true);
        }

        CamlElement ICamlConnector.NotIncludes(string fieldName, string listItemName)
        {
            return new CamlOperatorCommon(this, fieldName, listItemName, OperationKind.NotIncludes, false);
        }

        public CamlElement DateRangesOverlap(IListFieldInfoBase startDateFieldInfo, IListFieldInfoBase endDateFieldInfo, bool isRecurrence)
        {
            const FieldTypeKind fieldType = FieldTypeKind.DateTime;
            if (startDateFieldInfo.CamlQueryFieldType != fieldType ||
                endDateFieldInfo.CamlQueryFieldType != fieldType)
                throw new ArgumentException(
                    string.Format(FIELD_TYPE_ERROR_MESSAGE_FORMAT, fieldType));

            return new CamlOperatorDateRangesOverlap(this, startDateFieldInfo.Name, endDateFieldInfo.Name, isRecurrence);
        }

        public CamlElement DateRangesOverlap(string startDateFieldName, string endDateFieldName, bool isRecurrence)
        {
            return new CamlOperatorDateRangesOverlap(this, startDateFieldName, endDateFieldName, isRecurrence);
        }

        #endregion

        #region Connectors

        CamlConnector ICamlConnector.And(Func<ICamlConnector, CamlElement> getElement1, Func<ICamlConnector, CamlElement> getElement2)
        {
            return new CamlConnectorAnd(this, getElement1, getElement2);
        }

        CamlConnector ICamlConnector.Or(Func<ICamlConnector, CamlElement> getElement1, Func<ICamlConnector, CamlElement> getElement2)
        {
            return new CamlConnectorOr(this, getElement1, getElement2);
        }

        #endregion

        #endregion
    }
}
